#!/usr/bin/perl

# Module include
use Getopt::Std;

# Set argument parameters
getopts( "i:o:d:chs:f:t", \%opts ) or DisplayUsage();

# Verify arguments
DisplayHelp();
TestInputFileArg();

#TestOutputFileArg();
TestDateFormatArg();
TestSeparatorArg();

if ( $opts{o} ) {
	open( OUTPUT, ">$opts{o}" )
	  or die print "Error: could not open '$opts{o}'\n";

	# Insert header on top
	if ( $opts{t} ) {
		print OUTPUT SetHeader();
	}
}
else {

	# Insert header on top
	if ( $opts{t} ) {
		print SetHeader();
	}
}

# For each log file
foreach $i (@i) {

	# Open logfile
	open( LOGFILE, "$i" )
	  or die print "Error: '$i' not found in specified path\n";

	while (<LOGFILE>) {

		# Skip blank lines
		next unless ( !/^(\s)*$/ );

		# Advanced delimiter (not "[")
		if (/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/i) {
			chomp;
			$Line = $_;    # Current line
			EssDelimitedLog();
		}

		# Default delimiter ("[")
		elsif (/^\[(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/i) {
			chomp;
			$Line  = $_;           # Current line
			$Line2 = <LOGFILE>;    # Read next line
			chomp($Line2);
			EssClassicLog();
		}

		# Other case (description)
		else {
			chomp;
			$Line = $_;            # Current line
			Other();
		}

		if ( $opts{o} ) {
			if ( $Line =~ /.*$opts{f}.*$/ ) {
				print OUTPUT $Line . "\n";
			}
			else {

				#print OUTPUT $Line . "\n";
			}
		}
		else {
			if ( $Line =~ /.*$opts{f}.*$/ ) {
				print $Line. "\n";
			}
			else {

				#print $Line . "\n";
			}
		}
	}
	close(LOGFILE);
}
close(OUTPUT);

exit;

#------------------------------------------------------------
# FUNCTIONS
#------------------------------------------------------------
# Essbase v.5, 6, 7, 9 classic delimiters ([, ])
sub EssClassicLog {

	$Line =~ s/^\[//;                # Replace opening bracket ([) with nothing
	$Line =~ s/\]/$s/;               # Replace closing bracket (])
	$Line =~ s/\ /$s/ for 1 .. 4;    # Replace 4 1st whitespace
	$Line =~ s/\//$s/gm;             # Replace slash delimiters (/)

	ChangeDateFormat();

	if ( $opts{c} ) { AddMessageCategory(); }
	else {
		$Line =~ s/[()]/$s/ for 1 .. 2;    # Replace bracket delimiters ((,))
	}

	unless ( $Line2 =~ /^(\s)*$/ ) {       # Join with next line if not empty
		$Line = $Line . $Line2;
	}

	$Line =~ s/ +/ /gm;        # Replacing multi-space by single space
	$Line =~ s/[ \t]+$//gm;    # Deleting eol tab/space
}

#--------------------------------
# Essbase v. 6, 7, 9 advanced delimiters
# DELIMITEDMSG setting in config file with parameter: *, :, &, #, ~
sub EssDelimitedLog {

	my @f = split /[~^*&:]/, $Line;    # Split according to defined pattern
	                                   # and fill a list
	$lastf = scalar(@f);
	$Line  =
	    join( $s, @f[ 0 .. 3 ] ) . ':'
	  . join( ':', @f[ 4 .. 5 ] )
	  . $s
	  . join( $s,  @f[ 6 .. 11 ] )
	  . join( '',  @f[ 12 .. 13 ] ) . ' '
	  . join( ' ', @f[ 14 .. $lastf ] );

	ChangeDateFormat();

	if ( $opts{c} ) { AddMessageCategory(); }
	else {
		$Line =~ s/[()]/$s/ for 1 .. 2;    # Replace bracket delimiters ((,))
	}

	$Line =~ s/ +/ /gm;        # Replacing multi-space by single space
	$Line =~ s/[ \t]+$//gm;    # Deleting eol tab/space
}

#--------------------------------
sub Other {

	if ( $opts{d} ) {
		if ( $opts{c} ) {
			$Line = "$s$s$s$s$s$s$s$s$s$Line";
		}
		else {
			$Line = "$s$s$s$s$s$s$s$s$Line";
		}
	}
	else {
		if ( $opts{c} ) {
			$Line = "$s$s$s$s$s$s$s$s$s$s$s$s$Line";
		}
		else {
			$Line = "$s$s$s$s$s$s$s$s$s$s$s$Line";
		}
	}
	$Line =~ s/ +/ /gm;        # Replacing multi-space by single space
	$Line =~ s/\t//gm;         # Deleting tab
	$Line =~ s/[ \t]+$//gm;    # Deleting eol tab/space
}

#--------------------------------
sub DisplayHelp {

	if ( $opts{h} ) {
		print "DESCRIPTION:\n"
		  . "Parse ANY Essbase or Analytic Services (v.5-v.9) server and application logs.\n"
		  . "Generate a full custom delimited spreadsheet/database ready output\n"
		  . "for enhanced analysis.\n"
		  . "Options available: advanced date formatting, headers insertion,\n"
		  . "detailed message categories, filtering, custom separator.\n";
		print "AUTHOR:\n"
		  . "Written by Sebastien Roux <roux.sebastien@gmail.com>\n";
		print "LICENSE:\n"    
		  . "GNU General Public License version 3 (GPLv3)";
		print "NOTES:\n"
		  . "Use at your own risk!\n"
		  . "You will be solely responsible for any damage\n"
		  . "to your computer system or loss of data\n"
		  . "that may result from the download\n"
		  . "or the use of the following application/script.\n";
		DisplayUsage();
	}
}

#--------------------------------
sub DisplayUsage {

	print "\nUsage: perl EssbaseRightLog.pl -i <logfile(s)> "
	  . "[-o <outputfile>, -c, -d <arg>, -t, -s <arg>, -f <arg>, -h]\n\n";
	print "Usage: EssbaseRightLog.exe -i <logfile(s)> "
	  . "[-o <outputfile>, -c, -d <arg>, -t, -s <arg>, -f <arg>, -h]\n\n";
	print "  -i   specify Essbase log(s), args: <file1[;file2;...]>\n";
	print "  -o   specify output file\n";
	print "  -c   specify message categories\n";
	print "  -d   specify date format, arg: <ISO|EUR|US>\n";
	print "  -t   specify headers on top\n";
	print "  -s   specify separator, arg: <*>\n";
	print "  -f   specify filter, arg: <*>\n";
	print "  -h   display usage\n";
	exit;
}

#--------------------------------
sub TestInputFileArg {

	@i = split /;/, $opts{i};

	foreach $i (@i) {
		if ( !-e $i ) {
			print "Error: '$i' does not exists!\n";
			DisplayUsage();
		}
	}
}

#--------------------------------
sub TestOutputFileArg {

	if ( !-d $opts{o} ) {
		print "Error: '$opts{o}' is not a valid directory!\n";
		DisplayUsage();
	}
}

#--------------------------------
sub TestDateFormatArg {

	if (   $opts{d}
		&& ( uc( $opts{d} ) ne "ISO" )
		&& ( uc( $opts{d} ) ne "EUR" )
		&& ( uc( $opts{d} ) ne "US" ) )
	{
		print "Error: '$opts{d}' is not a valid argument for date format";
		DisplayUsage();
	}
}

#--------------------------------
# Add category for message code
sub AddMessageCategory {

	my $MsgCode;
	my $MsgType;

	if ( $Line =~ m/\d{7,7}/ ) {
		$MsgCode = $&;

		if ( $MsgCode ge 1003000 && $MsgCode le 1003040 ) {
			$MsgType = "Data Load";
		}
		elsif ( $MsgCode ge 1006002 && $MsgCode le 1006051 ) {
			$MsgType = "Data Cache";
		}
		elsif ( $MsgCode ge 1008001 && $MsgCode le 1008152 ) {
			$MsgType = "System Call";
		}
		elsif ( $MsgCode ge 1012000 && $MsgCode le 1012750 ) {
			$MsgType = "Calculation";
		}
		elsif ( $MsgCode ge 1014004 && $MsgCode le 1014043 ) {
			$MsgType = "Lock Manager";
		}
		elsif ( $MsgCode ge 1019002 && $MsgCode le 1019054 ) {
			$MsgType = "Db Objects";
		}
		elsif ( $MsgCode ge 1040002 && $MsgCode le 1042025 ) {
			$MsgType = "Network";
		}
		elsif ( $MsgCode ge 1052001 && $MsgCode le 1054030 ) {
			$MsgType = "Agent";
		}
		elsif ( $MsgCode ge 1120000 && $MsgCode le 1120002 ) {
			$MsgType = "Grid API";
		}
		elsif ( $MsgCode ge 1130203 && $MsgCode le 1130613 ) {
			$MsgType = "Misc";
		}
		elsif ( $MsgCode ge 1270006 && $MsgCode le 1270517 ) {
			$MsgType = "ASO";
		}
		else { $MsgType = "Other"; }
		$Line =~ s/\(\d{7,7}\)/$s$MsgCode$s$MsgType$s/;
	}
}

#--------------------------------
sub GetTime {

	( $sec, $min, $hour, $day, $month, $year ) = localtime(time);
	my $files = printf "%4d%02d%02d_%02d%02d%02d", $year + 1900, $month + 1,
	  $day, $hour, $min, $sec;
	print "$files\n";
	return $files;
}

#--------------------------------
sub ChangeDateFormat {

	ChangeMonthString();

	my @l = split /[$s]/, $Line;
	my $last;

	# Set date format to ISO 8601 extended style (YYYY-MM-DD)
	if ( uc( $opts{d} ) eq "ISO" ) {
		$last = scalar(@l);
		$Line =
		    @l[ 4 .. 4 ] . '-'
		  . @l[ 1 .. 1 ] . '-'
		  . @l[ 2 .. 2 ]
		  . $s
		  . join( $s, @l[ 3 .. 3 ] )
		  . $s
		  . join( $s, @l[ 5 .. $last - 1 ] );
	}

	# Set date format to US style (MM/DD/YYYY)
	elsif ( uc( $opts{d} ) eq "US" ) {
		$last = scalar(@l);
		$Line =
		    @l[ 1 .. 1 ] . '/'
		  . @l[ 2 .. 2 ] . '/'
		  . @l[ 4 .. 4 ]
		  . $s
		  . join( $s, @l[ 3 .. 3 ] )
		  . $s
		  . join( $s, @l[ 5 .. $last - 1 ] );
	}

	# Set date format to European style (DD/MM/YYYY)
	elsif ( uc( $opts{d} ) eq "EUR" ) {
		$last = scalar(@l);
		$Line =
		    @l[ 2 .. 2 ] . '/'
		  . @l[ 1 .. 1 ] . '/'
		  . @l[ 4 .. 4 ]
		  . $s
		  . join( $s, @l[ 3 .. 3 ] )
		  . $s
		  . join( $s, @l[ 5 .. $last - 1 ] );
	}
}

#--------------------------------
# Replace month label by month number
sub ChangeMonthString {

	my $MonthIndex;

	if ( $Line =~ m/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/ ) {
		if    ( lc($&) eq "jan" ) { $MonthIndex = "01"; }
		elsif ( lc($&) eq "feb" ) { $MonthIndex = "02"; }
		elsif ( lc($&) eq "mar" ) { $MonthIndex = "03"; }
		elsif ( lc($&) eq "apr" ) { $MonthIndex = "04"; }
		elsif ( lc($&) eq "may" ) { $MonthIndex = "05"; }
		elsif ( lc($&) eq "jun" ) { $MonthIndex = "06"; }
		elsif ( lc($&) eq "jul" ) { $MonthIndex = "07"; }
		elsif ( lc($&) eq "aug" ) { $MonthIndex = "08"; }
		elsif ( lc($&) eq "sep" ) { $MonthIndex = "09"; }
		elsif ( lc($&) eq "oct" ) { $MonthIndex = "10"; }
		elsif ( lc($&) eq "nov" ) { $MonthIndex = "11"; }
		elsif ( lc($&) eq "dec" ) { $MonthIndex = "12"; }
		$Line =~ s/$&/$MonthIndex/;
	}
}

#--------------------------------
# Set default separator (|) if separator arg not specified
sub TestSeparatorArg {

	my $defaultseparator = "|";

	if ( not( $opts{s} ) ) { $s = $defaultseparator; }
	else { $s = $opts{s}; }

	return $s;
}

#--------------------------------
sub SetHeader {

	my $Header;

	if ( $opts{d} ) {
		if ( $opts{c} ) {
			$Header =
			    "date" . $s . "time" . $s . "server" . $s
			  . "application"
			  . $s
			  . "database"
			  . $s . "user"
			  . $s
			  . "msglevel"
			  . $s
			  . "msgcode"
			  . $s
			  . "msgcat"
			  . $s
			  . "description\n";
		}
		else {
			$Header =
			    "date" . $s . "time" . $s . "server" . $s
			  . "application"
			  . $s
			  . "database"
			  . $s . "user"
			  . $s
			  . "msglevel"
			  . $s
			  . "msgcode"
			  . $s
			  . "description\n";
		}
	}
	else {
		if ( $opts{c} ) {
			$Header =
			    "day" . $s . "month" . $s . "daynum" . $s . "time" . $s . "year"
			  . $s
			  . "server"
			  . $s
			  . "application"
			  . $s
			  . "database"
			  . $s . "user"
			  . $s
			  . "msglevel"
			  . $s
			  . "msgcode"
			  . $s
			  . "msgcat"
			  . $s
			  . "description\n";
		}
		else {
			$Header =
			    "day" . $s . "month" . $s . "daynum" . $s . "time" . $s . "year"
			  . $s
			  . "server"
			  . $s
			  . "application"
			  . $s
			  . "database"
			  . $s . "user"
			  . $s
			  . "msglevel"
			  . $s
			  . "msgcode"
			  . $s
			  . "description\n";
		}
	}
	return $Header;
}
__END__
